package com.sunseaypw.springboot;

import java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.DefaultErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import reactor.core.publisher.Mono;

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }    
    
    @RestController
    public static class DemoController{
        @GetMapping("/demo") //跨域注解式配置
        @CrossOrigin("https://domain2.com") 
        Mono<String> demo(){
            return Mono.just("jkjkjkjk");
        }
    }
    
    /***默认有一个RouterFunction handlemapper/handleadapter */
    @Bean
    public RouterFunction<ServerResponse> toHelloRouter(HelloHandler handler) {
        return RouterFunctions
                .route(RequestPredicates.GET("/hello")
                        .and(RequestPredicates.accept(MediaType.TEXT_PLAIN))
                    ,handler::hello);
    }

    /**
     * 只有前置过滤器的概念，没有后置过滤器的概念
     * 倒置执行filter，任何一个直接返回response则短路
     */
    @Bean
    public RouterFunction<ServerResponse> toHello2Router() {
        return RouterFunctions.route(
                RequestPredicates.GET("/hello2").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                request -> ServerResponse.ok().body(BodyInserters.fromObject("hello222222 啊！！！"))
            ).filter((serverRequest, handlerFunction) -> {
                // 针对/hello 的请求进行过滤，然后在响应中添加一个Content-Type属性
                // return ServerResponse.status(HttpStatus.OK)
                //     .header("Content-Type","text/plain; charset=utf-8")
                //     .body(BodyInserters.fromObject("真的好啊11111111111！！！"));
                
                serverRequest.attributes().put("handle-flag111", "此为打发打发啊，你注意处理啊222222222");
                return handlerFunction.handle(serverRequest);
            }).filter((serverRequest, handlerFunction) -> {
                // 针对/hello 的请求进行过滤，然后在响应中添加一个Content-Type属性
                // return ServerResponse.status(HttpStatus.OK)
                //     .header("Content-Type","text/plain; charset=utf-8")
                //     .body(BodyInserters.fromObject("真的好啊22222222222！！！"));
                
                serverRequest.attributes().put("handle-flag2222", "此为打发打发啊，你注意处理啊3333333333333");
                return handlerFunction.handle(serverRequest);
            });
    }
    
    @Bean
    public RouterFunction<ServerResponse> toErrorRouter() {
        return RouterFunctions
                .route(RequestPredicates.GET("/error")
                        .and(RequestPredicates.accept(MediaType.TEXT_PLAIN))
                    ,(req)->{
                       return ServerResponse.ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .header("Content-Type","text/plain; charset=utf-8")
                        .body(BodyInserters.fromObject("Hello, 你错了！！"));
                    });
    }

    @Bean
    public RouterFunction<ServerResponse> toExceptionRouter() {
        return RouterFunctions
                .route(RequestPredicates.GET("/exception")
                        .and(RequestPredicates.accept(MediaType.TEXT_PLAIN))
                    ,(req)->{
                       if(req.path()!="")
                          throw new RuntimeException("就是让你发生错误的啊！！！");
                       return ServerResponse.ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .header("Content-Type","text/plain; charset=utf-8")
                        .body(BodyInserters.fromObject("Hello, 你错了！！"));
                    });
    }

    @Component
    @Order(-1)
    public class TokenWebFilter implements WebFilter {
        @Override
        public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
            //ServerWebExchange 是 WebFlux 的一个服务交换器 类似于ServletContext
            ServerHttpRequest request =  serverWebExchange.getRequest();
            //获取请求http头token值
            String host = request.getHeaders().getFirst("host");
            System.out.println("host ＝" + host);    
            String token = request.getHeaders().getFirst("token");
            //可在链路上传递消息，以便各个节点基于此进行相应的逻辑判断和处理
            //切记这个不是response上的属性啊！！！
            serverWebExchange.getAttributes().put("handled", "已经被我处理了啊，天王盖地虎");        
            if("/hello".equals(request.getURI().getPath()) && null == token){
                System.out.println("被过滤了啊");                    
                /*http头中没有appKey，修改请求的目标url为/auth/error
                    request.mutate返回一个请求构建器(builder design pattern)，path方法修改请求的url，build方法返回新的request
                */
                ServerHttpRequest authErrorReq = request.mutate().path("/error").build();
                //erverWebExchange.mutate类似，构建一个新的ServerWebExchange
                ServerWebExchange authErrorExchange = serverWebExchange.mutate().request(authErrorReq).build();
                return webFilterChain.filter(authErrorExchange).doFinally(signal ->  System.out.println("signal111111111="+signal.toString()));      
            } else{                
                /*过滤器链的概念都是类似的，调用过滤器链的filter方法将请求转到下一个filter，
                如果该filter是最后一  个filter,那就转到该请求对应的handler,
                也就是controller方法或函数式endpoint */
                var m=webFilterChain.filter(serverWebExchange);
                //后处理                  
                return m.doFinally(signal ->  System.out.println("signal22222222="+signal.toString()));      
            }
        }
    }

    @Component
    @Order(0)
    public class ExtraWebFilter implements WebFilter {
        @Override
        public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
            System.out.println("exchange 暗号 ＝" + serverWebExchange.getAttributes().get("handled"));    
            return webFilterChain.filter(serverWebExchange);
        }
    }
    
    @Component
    public class HelloHandler {
        public Mono<ServerResponse> hello(ServerRequest request) {
            return ServerResponse.ok()
                .contentType(MediaType.TEXT_PLAIN)
                .header("Content-Type","text/plain; charset=utf-8")
                .body(BodyInserters.fromObject("Hello, 你是谁啊"));

        }
    }

    /** webflux 全局异常封装处理 */
    @Component
    @Order(-2) 
    public class GlobalErrorAttributes extends DefaultErrorAttributes {
        public GlobalErrorAttributes() {
            super(false);
        }
        @Override
        public Map<String, Object> getErrorAttributes(ServerRequest request,
                boolean includeStackTrace) {
            Map<String, Object> errorAttributes = super.getErrorAttributes(
                    request, includeStackTrace);
            Throwable error = getError(request);
            // 自定义一个 666
            errorAttributes.put("code", 666);
            errorAttributes.put("data", " ----------- you are error !!! "+error.getMessage());
            return errorAttributes;
        }
    }
    @Component
    @Order(-3)
    public class GlobalErrorWebExceptionHandler222 extends AbstractErrorWebExceptionHandler {
    
        public GlobalErrorWebExceptionHandler222(GlobalErrorAttributes g, ApplicationContext applicationContext,
                ServerCodecConfigurer serverCodecConfigurer) {
            super(g, new ResourceProperties(), applicationContext);
            super.setMessageWriters(serverCodecConfigurer.getWriters());
            super.setMessageReaders(serverCodecConfigurer.getReaders());
        }    
        @Override
        protected RouterFunction<ServerResponse> getRoutingFunction(final ErrorAttributes errorAttributes) {
            return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
        }    
        private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {    
            final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);    
            return ServerResponse.status(HttpStatus.BAD_REQUEST)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject(errorPropertiesMap));
        }    
    }
    
}